/**
 * #info
 *
 *@author   wuwg
 *@createTime   2018/03/20
 *@updateTime   2018/04/23
 *@description  table-column  component  表格列组件
 */
/**
 * #item
 *
 * @type        aty-table-column
 * @cn            表格列
 * @nameprop    label
 * @stPlugin    false
 * @stContainer true
 * @parents     [aty-table]
 */

import config from '../../../../config/config.js'

// components
import tag from '../../../data/tag/index.vue'
// tools
import {nullOrUndefined, nullOrUndefinedThen} from "../unit/util.js";

// variable
let columnIdSeed = 1

const defaults = {
  default: {
    order: ''
  },
  radio: {
    width: 48,
    minWidth: 48,
    realWidth: 48,
    order: '',
  },
  checkbox: {
    width: 48,
    minWidth: 48,
    realWidth: 48,
    order: '',
    className: 'aty-table-column--selection'
  },
  selection: {
    width: 48,
    minWidth: 48,
    realWidth: 48,
    order: '',
    className: 'aty-table-column--selection'
  },
  expand: {
    width: 48,
    minWidth: 48,
    realWidth: 48,
    order: ''
  },
  index: {
    width: 48,
    minWidth: 48,
    realWidth: 48,
    order: ''
  }
}

const forced = {
  radio: {
    renderHeader: function (h, { column }) {
      return column.label || ''
    },
    renderCell: function (h, { row, column, store, $index }) {
      return <radio
        checked={store.isCurrent(row)}
        disabled={column.selectable ? !column.selectable.call(null, row, $index) : false}
      ></radio>
    },
    // 是否支持排序
    sortable: false,
    // 是否支持resize
    resizable: false
  },
  checkbox: { // 和type=selection逻辑一样
    renderHeader: function (h, { store }) {
      return <checkbox
        disabled={store.states.data && store.states.data.length === 0}
        indeterminate={store.states.selection.length > 0 && !this.isAllSelected}
        select-on-indeterminate={store.states.selectOnIndeterminate}
        checked={this.isAllSelected}
        on-change={this.toggleAllSelection}
      ></checkbox>
    },
    renderCell: function (h, { row, column, store, $index }) {
      return <checkbox
        nativeOn-click={(event) => event.stopPropagation()}
        checked={store.isSelected(row)}
        disabled={column.selectable ? !column.selectable.call(null, row, $index) : false}
        on-change={() => {
          store.commit('rowSelectedChanged', row)
        }}
      ></checkbox>
    },
    // 是否支持排序
    sortable: false,
    // 是否支持resize
    resizable: false
  },
  selection: {// 和type=checkbox逻辑一样
    renderHeader: function (h, { store }) {
      return <checkbox
        disabled={store.states.data && store.states.data.length === 0}
        indeterminate={store.states.selection.length > 0 && !this.isAllSelected}
        select-on-indeterminate={store.states.selectOnIndeterminate}
        checked={this.isAllSelected}
        on-change={this.toggleAllSelection}
      ></checkbox>
    },
    renderCell: function (h, { row, column, store, $index }) {
      return <checkbox
        nativeOn-click={(event) => event.stopPropagation()}
        checked={store.isSelected(row)}
        disabled={column.selectable ? !column.selectable.call(null, row, $index) : false}
        on-change={() => {
          store.commit('rowSelectedChanged', row)
        }}
      ></checkbox>
    },
    // 是否支持排序
    sortable: false,
    // 是否支持resize
    resizable: false
  },
  index: {
    renderHeader: function (h, { column }) {
      return column.label || '序号'
    },
    renderCell: function (h, { $index, column }) {
      let i = $index + 1
      const index = column.index

      if (typeof index === 'number') {
        i = $index + index
      } else if (typeof index === 'function') {
        i = index($index)
      }

      return i
    },
    sortable: false
  },
  expand: {
    renderHeader: function (h, { column }) {
      return column.label || ''
    },
    renderCell: function (h, { row, store }, proxy) {
      const expanded = store.states.expandRows.indexOf(row) > -1
      return <i class={'aty-icon aty-icon-chevron-right ' + (expanded ? 'expand-icon-expanded' : '')}  on-click={e => proxy.handleExpandClick(row, e)}></i>
    },
    sortable: false,
    resizable: false
  }
}

const getDefaultColumn = function (type, options) {
  const column = {}
  Artery.merge(column, defaults[type || 'default'])
  for (const name in options) {
    if (options.hasOwnProperty(name)) {
      const value = options[name]
      if (!nullOrUndefined(value)) {
        column[name] = value
      }
    }
  }
  if (!column.minWidth) {
    column.minWidth = 80
  }
  column.realWidth = nullOrUndefinedThen(column.width, column.minWidth)
  return column
}

const DEFAULT_RENDER_CELL = function (h, { row, column }) {
  const property = column.property
  let value
  if (row.hasOwnProperty(property)) {
    if (row.hasOwnProperty(property + 'TranslateText')) {
      value = row[property + 'TranslateText']
    } else {
      value = row[property]
    }
  } else {
    value = property && Artery.getPropByPath(row, property + 'TranslateText').v
    if (!value) {
      value = property && Artery.getPropByPath(row, property).v
    }
  }

  if (column && column.formatter) {
    if (typeof column.formatter === 'function') {
      return column.formatter(value, row, column)
    }
  }
  return value
}

const parseWidth = (width) => {
  if (!nullOrUndefined(width)) {
    width = parseInt(width, 10)
    if (isNaN(width)) {
      width = null
    }
  }
  return width
}

const parseMinWidth = (minWidth) => {
  if (!nullOrUndefined(minWidth)) {
    minWidth = parseInt(minWidth, 10)
    if (isNaN(minWidth)) {
      minWidth = 80
    }
  }
  return minWidth
}

// module
export default {
  name: config.prefix + '-table-column',
  components: {
    tag
  },
  props: {

    /**
     * #property
     *
     * @name    type
     * @cn      列的类型
     * @group   样式
     * @type    list
     * @value   default
     * @options [default, radio, checkbox, selection, index, expand, data]
     * @description 对应列的类型，取值为 [default, radio, checkbox, selection, index, expand, data]中的其中一个
     * 如果设置了 radio 则显示单选框；
     * 如果设置了 checkbox 则显示多选框，效果同selection一样；
     * 如果设置了 selection 则显示多选框，效果同checkbox一样；
     * 如果设置了 index 则显示该行的索引（从 1 开始计算）；
     * 如果设置了 expand 则显示为一个可展开的按钮；
     * 如果设置了 data 则页面不渲染该列，但导出Excel时，会带有该列。
     */
    type: {
      type: String,
      default: 'default'
    },
    /**
     * #property
     *
     * @name    label
     * @cn      显示的标题
     * @group   样式
     * @type    string
     * @description   列的标题。
     */
    label: String,
    /**
     * #property
     *
     * @name    className
     * @cn      列的className
     * @group   样式
     * @type    string
     * @description   列的className。
     */
    className: String,
    /**
     * #property
     *
     * @name    labelClassName
     * @cn      列标题的className
     * @group   样式
     * @type    string
     * @description   列标题Label区域的className。
     */
    labelClassName: String,
    /**
     * #property
     *
     * @name    prop
     * @cn      对应列内容的字段名。
     * @group   数据
     * @type    string
     * @description   对应列内容的字段名。
     */
    prop: String,
    /**
     * #property
     *
     * @name    width
     * @cn      对应列的宽度
     * @group   样式
     * @type    string,number
     * @description   对应列的宽度。
     */
    width: {
      type: [String, Number]
    },
    /**
     * #property
     *
     * @name    minWidth
     * @cn      对应列的最小宽度
     * @group   样式
     * @type    string,number
     * @description   对应列的最小宽度，与width的区别是width是固定的，min-width 会把剩余宽度按比例分配给设置了 min-width 的列。
     */
    minWidth: {
      type: [String, Number]
    },
    /**
     * #property
     *
     * @name    renderHeader
     * @cn      列标题Label区域渲染使用的 Function
     * @group   表头
     * @type    function
     * @bind  must
     * @description  列标题Label区域渲染使用的 Function(h, { column, $index })。
     */
    renderHeader: Function,
    /**
     * #property
     *
     * @name    sortable
     * @cn      对应列是否可以排序
     * @group   过滤
     * @type    boolean
     * @value   false
     * @description  对应列是否可以排序。
     */
    sortable: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    resizable
     * @cn      大小可变
     * @group   控制
     * @type    boolean
     * @bind    must
     * @value   true
     * @description  对应列是否可以通过拖动改变宽度（需要在 aty-table 上设置 border 属性为 true）。
     */
    resizable: {
      type: Boolean,
      default: true
    },
    context: {},
    /**
     * #property
     *
     * @name    align
     * @cn      对齐方式
     * @group   样式
     * @type    list
     * @options [left,center,right]
     * @description 单元格对齐方式，取值为 ['left,center,right']中的一个。
     */
    align: String,
    /**
     * #property
     *
     * @name    headerAlign
     * @cn      表头对齐方式
     * @group   表头
     * @type    list
     * @options [left,center,right]
     * @description 表头对齐方式，若不设置该项，则使用单元格的对齐方式，取值为 ['left,center,right']中的一个。
     */
    headerAlign: String,
    /**
     * #property
     *
     * @name    showOverflowTooltip
     * @cn      当内容过长被隐藏时显示 tooltip
     * @group   样式
     * @type    boolean
     * @bind    must
     * @value   true
     * @description 当内容过长被隐藏时显示tooltip。
     */
    showOverflowTooltip: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    fixed
     * @cn      列是否固定在左侧或者右侧
     * @group   控制
     * @type    list
     * @options [left, right]
     * @description 列是否固定在左侧或者右侧
     */
    fixed: String,
    /**
     * #property
     *
     * @name    formatter
     * @cn      仅对type=default的列有效，用来格式化内容
     * @group   数据
     * @type    function
     * @bind   must
     * @description 仅对type=default的列有效，类型为 Function，Function(value, row, column)，用来格式化内容。
     */
    formatter: Function,
    /**
     * #property
     *
     * @name    selectable
     * @cn      CheckBox 是否可以勾选
     * @group   控制
     * @type    function
     * @bind    must
     * @description 仅对 type=selection 的列有效，类型为 Function，Function(row, index) 的返回值用来决定这一行的 CheckBox 是否可以勾选。
     */
    selectable: Function,
    /**
     * #property
     *
     * @name    reserveSelection
     * @cn      反选
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 仅对 type=selection 的列有效，类型为 Boolean，为 true 则会在数据更新之后保留之前选中的数据（ary-table 须指定 row-key 属性）。
     */
    reserveSelection: {
      type: Boolean,
      default: false
    },

    /**
     * #property
     *
     * @name    index
     * @cn      属性来自定义索引
     * @group   数据
     * @type    number,function
     * @bind    must
     * @description  如果设置了 type=index，可以通过传递 index 属性来自定义索引，类型为 Number 或 Function(index)。
     */
    index: [Number, Function],
    /**
     * #property
     * @name    filterType
     * @cn      过滤类型
     * @type     list
     * @group    过滤
     * @bind    mustnot
     * @options [string,number,int,short,code,daterange,user,dept,corp,organ_all]
     * @description 可取值[string,number,int,short,code,daterange,user,dept,corp,organ_all]
     */
    filterType: {
      validator (value) {
        return Artery.isOneOf(value, ['', 'string', 'number', 'int', 'short', 'code', 'daterange', 'user', 'dept', 'corp', 'organ_all'])
      },
      default: ''
    },
    filterNumberType: {

    },
    /**
     * #property
     *
     * @name    filterTriggerOnSide
     * @cn      过滤触发按钮在表头文字旁边，而不是在文字上方
     * @type    boolean
     * @group    过滤
     * @value   false
     * @description 过滤触发按钮在表头文字旁边，而不是在文字上方。
     */
    filterTriggerOnSide: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    filterPopupClass
     * @cn      设置点击触发过滤按钮后，下拉框的样式类名
     * @type    string
     * @description 仅当filterTriggerOnSide=true时有效，设置点击触发过滤按钮后，下拉框的样式类名。
     */
    filterPopupClass: {
      type: String
    },
    /**
     * #property
     * @name    filterMax
     * @type     number
     * @group    过滤
     * @bind    must
     * @description 仅对filterType=number时有效，设置数值过滤的最大值。
     */
    filterMax: Number,
    /**
     * #property
     * @name    filterMin
     * @type     number
     * @group    过滤
     * @bind    must
     * @description 仅对filterType=number时有效，设置数值过滤的最小值。
     */
    filterMin: Number,
    /**
     * #property
     * @name    filterStep
     * @type     number
     * @group    过滤
     * @bind    must
     * @description 仅对filterType=number时有效，设置数值过滤的步长。
     */
    filterStep: Number,
    /**
     * #property
     *
     * @name    codeType
     * @cn      代码类型
     * @group   数据
     * @type    string
     * @description 仅对filterType=code时有效，设置代码值过滤的代码类型。
     */
    codeType: {
      type: String,
      default: null
    },
    /**
     * #property
     *
     * @name    filterMultiple
     * @cn      数据过滤的选项是否多选
     * @type    boolean
     * @bind    must
     * @group    过滤
     * @value   true
     * @description filterType=[code,user,dept,corp,organ_all]时有效，设置代码值过滤的选项是否可以多选。
     */
    filterMultiple: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    filterLazy
     * @cn      过滤下拉延迟加载
     * @group   过滤
     * @type    boolean
     * @bind    must
     * @value   true
     * @description 仅对filterType=code时有效，设置代码值过滤的代码，是否延迟加载。
     */
    filterLazy: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    filterOrganScope
     * @cn      organ-tree的selectScope
     * @group   过滤
     * @type    list
     * @options  [all, csCorp, corp, csDept, dept]
     * @value   all
     * @description filterType=[user,dept,corp,organ_all]时有效，设置组织机构过滤的selectScope，取值为[all-全部， csCorp-本单位和下级单位，corp-本单位， csDept-本部门和子部门， dept-本部门，cpCorp- 本单位及上级单位]。
     */
    filterOrganScope: {
      type: String,
      default: 'all'
    },
    /**
     * #property
     *
     * @name    filterSearchShowLimit
     * @cn      过滤搜索框显示搜索结果数量限制
     * @group   过滤
     * @type    number
     * @bind    must
     * @value   5
     * @description 过滤搜索框显示搜索结果的数量限制,如果调大搜素框会出现滚动条可能影响效果展示
     */
    filterSearchShowLimit: {
      type: Number,
      default: 5
    },
    /**
     * #property
     *
     * @name    exportable
     * @cn      对应列是否可以导出到excel
     * @group   导出
     * @type    boolean
     * @value   true
     * @bind    must
     * @description  对应列是否可以导出到excel。
     */
    exportable: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    isRender
     * @cn      是否渲染
     * @group   控制
     * @type    boolean
     * @value   true
     * @bind    must
     * @description  是否渲染该列，用户动态列的实现。
     */
    isRender: {
      type: Boolean,
      default: true
    }
  },

  data () {
    return {
      isSubColumn: false,
      columns: [],
      columnReady: false
    }
  },

  beforeCreate () {
    this.row = {}
    this.column = {}
    this.$index = 0
  },
  watch: {
    label (newVal) {
      if (this.columnConfig) {
        this.columnConfig.label = newVal
      }
    },

    prop (newVal) {
      if (this.columnConfig) {
        this.columnConfig.property = newVal
      }
    },

    filterMultiple (newVal) {
      if (this.columnConfig) {
        this.columnConfig.filterMultiple = newVal
      }
    },

    align (newVal) {
      if (this.columnConfig) {
        this.columnConfig.align = newVal ? 'is-' + newVal : null

        if (!this.headerAlign) {
          this.columnConfig.headerAlign = newVal ? 'is-' + newVal : null
        }
      }
    },

    headerAlign (newVal) {
      if (this.columnConfig) {
        this.columnConfig.headerAlign = 'is-' + (newVal || this.align)
      }
    },

    width (newVal) {
      if (this.columnConfig) {
        this.columnConfig.width = parseWidth(newVal)
        this.owner.store.scheduleLayout()
      }
    },

    minWidth (newVal) {
      if (this.columnConfig) {
        this.columnConfig.minWidth = parseMinWidth(newVal)
        this.owner.store.scheduleLayout()
      }
    },

    fixed (newVal) {
      if (this.columnConfig) {
        this.columnConfig.fixed = newVal
        this.owner.store.scheduleLayout(true)
      }
    },

    sortable (newVal) {
      if (this.columnConfig) {
        this.columnConfig.sortable = newVal
      }
    },

    index (newVal) {
      if (this.columnConfig) {
        this.columnConfig.index = newVal
      }
    },

    formatter (newVal) {
      if (this.columnConfig) {
        this.columnConfig.formatter = newVal
      }
    },

    isRender (newVal) {
      if (this.columnConfig) {
        this.columnConfig.isRender = newVal
        if (newVal) {
          this.insertColumn()
        } else {
          this.removeColumn()
        }
        this.owner.store.scheduleLayout(true)
      }
    }
  },

  computed: {
    owner () {
      let parent = this.$parent
      while (parent && !parent.tableId) {
        parent = parent.$parent
      }
      return parent
    },
    columnOrTableParent () {
      let parent = this.$parent
      while (parent && !parent.tableId && !parent.columnId) {
        parent = parent.$parent
      }
      return parent
    }
  },

  created () {
    this.customRender = this.$options.render
    this.$options.render = (h) => h('div', this.$slots.default)
    const parent = this.columnOrTableParent
    const owner = this.owner
    this.isSubColumn = owner !== parent
    this.columnId = (parent.tableId || parent.columnId) + '_column_' + columnIdSeed++

    const type = this.type

    const isColumnGroup = false

    // 栏目默认options
    const column = getDefaultColumn(type, {
      id: this.columnId,
      label: this.label,
      className: this.className,
      labelClassName: this.labelClassName,
      property: this.prop,
      type,
      renderCell: null,
      renderHeader: this.renderHeader,
      minWidth: parseMinWidth(this.minWidth),
      width: parseWidth(this.width),
      isColumnGroup,
      context: this.context,
      align: this.align ? 'is-' + this.align : null,
      headerAlign: this.headerAlign ? 'is-' + this.headerAlign : (this.align ? 'is-' + this.align : null),
      sortable: this.sortable === '' ? true : this.sortable,
      resizable: this.resizable,
      showOverflowTooltip: this.showOverflowTooltip,
      formatter: this.formatter,
      selectable: this.selectable,
      reserveSelection: this.reserveSelection,
      fixed: this.fixed === '' ? true : this.fixed,
      filterMultiple: this.filterMultiple,
      filterOpened: false,
      filterType: this.filterType,
      filterMax: this.filterMax,
      filterMin: this.filterMin,
      filterStep: this.filterStep,
      index: this.index,
      filterValue: '',
      showFilterTip: false,
      codeType: this.codeType,
      filterLazy: this.filterLazy,
      filterOrganScope: this.filterOrganScope,
      filterSearchShowLimit: this.filterSearchShowLimit,
      $slots: {
        filter: this.$slots.filter
      },
      exportable: this.exportable === '' ? true : this.exportable,
      filterTriggerOnSide: this.filterTriggerOnSide,
      filterPopupClass: this.filterPopupClass,
      isRender: this.isRender
    })
    Artery.merge(column, forced[type] || {})

    this.columnConfig = column

    let renderCell = column.renderCell
    const _self = this

    if (type === 'expand') {
      owner.renderExpanded = function (h, data) {
        return _self.$scopedSlots.default ? _self.$scopedSlots.default(data) : _self.$slots.default
      }
      column.renderCell = function (h, data) {
        return renderCell(h, data, this._renderProxy)
      }
      return
    }

    column.renderCell = function (h, data) {
      if (_self.$scopedSlots.default) {
        renderCell = () => _self.$scopedSlots.default(data)
      }
      if (!renderCell) {
        renderCell = DEFAULT_RENDER_CELL
      }
      return renderCell(h, data)
    }
  },

  destroyed () {
    this.removeColumn()
  },

  updated () {
    if (this.columnReady) {
      this.columnConfig.$slots.filter = this.$slots.filter
      this.owner.store.commit('updateHeader')
    }
  },

  mounted () {
    this.insertColumn()
  },
  methods: {
    insertColumn () {
      const owner = this.owner
      const parent = this.columnOrTableParent
      let columnIndex
      if (!this.isSubColumn) {
        columnIndex = [].indexOf.call(parent.$refs.hiddenColumns.children, this.$el)
      } else {
        columnIndex = [].indexOf.call(parent.$el.children, this.$el)
      }
      if (this.columnConfig.isRender) {
        owner.store.commit('insertColumn', this.columnConfig, columnIndex, this.isSubColumn ? parent.columnConfig : null)
      }
      this.columnReady = true
    },
    removeColumn () {
      if (!this.$parent) {
        return
      }
      const parent = this.$parent
      this.owner.store.commit('removeColumn', this.columnConfig, this.isSubColumn ? parent.columnConfig : null)
    }
  }
}
